#include "biblioteca.h"
#include "database.h"

extern int training;
extern int classe_procurada;

//TODO: separar treinamento e teste de acordo com o especificado no trabalho

//TODO: alterar essa função
int populacao_contem_individuo(DP_INDI population, P_INDI indiv, int index){
    register int i  = 0;
    for ( ; i < index; ++i)
        if ( compara_igualdade_individuos(population[i], indiv) )
            return 1;
    return 0;
}

//TODO: recriar essa funçao
void avaliar_individuo(P_INDI pointer){
    void calcula_indicadores(P_INDI pointer){
        register int i = 0;
        //i < numero de linhas
	    //int valendo;// = 1;
	    //if ( training == 1 ) {
	    for( i = 0; i < ( training == 1 ? TRAINING_SIZE : TEST_SIZE ); ++i){
	        //for( i = 0; i < TRAINING_SIZE; ++i){
		    int valendo = 1;
            register int j;
		    //int valendo = 1;
            for ( j = 0; j < SIZE_MATERIAL; ++j){
                //if ( pointer->atributos[j].presente != 0 ){
                //printf("Peso: %d\n",pointer->atributos[j].peso);
                if ( pointer->atributos[j].peso >= 7 ){
                    switch ( pointer->atributos[j].operador ){
                        case IGUAL:
                            valendo = ( training == 1 ) ? ( ( pointer->atributos[j].valor == database_training[i][j] ) ? 1 : 0 ) : ( ( pointer->atributos[j].valor == database_test[i][j] ) ? 1 : 0 );
                            break;
                        case DIFERENTE:
                            valendo = ( training == 1 ) ? ( ( pointer->atributos[j].valor != database_training[i][j] ) ? 1 : 0 ) : ( ( pointer->atributos[j].valor != database_test[i][j] ) ? 1 : 0 );
                            break;
                        case MAIOR_IGUAL:
                            valendo = ( training == 1 ) ? ( ( database_training[i][j] >= pointer->atributos[j].valor  ) ? 1 : 0 ) : ( ( pointer->atributos[j].valor >= database_test[i][j] ) ? 1 : 0 );
                            break;
                        case MENOR:
                            valendo = ( training == 1 ) ? ( ( pointer->atributos[j].valor > database_training[i][j] ) ? 1 : 0 ) : ( ( pointer->atributos[j].valor < database_training[i][j] ) ? 1 : 0 );
                            break;
                    }
                }
            }
	        if ( valendo ){
	            if ( ( training == 1 ) ? ( classe_procurada == database_training[i][SIZE_MATERIAL] ) : ( classe_procurada == database_test[i][SIZE_MATERIAL] ) ){
        	        pointer->true_positive = pointer->true_positive + 1;
                }else{
                    pointer->false_positive = pointer->false_positive + 1;
                }
            }else{
                if ( ( training == 1 ) ? ( classe_procurada == database_training[i][SIZE_MATERIAL] ) : ( classe_procurada == database_test[i][SIZE_MATERIAL] ) ){
                    pointer->false_negative = pointer->true_positive + 1;
                }else{
                    pointer->true_negative = pointer->false_positive + 1;
                }
            }
        }
    }
/*	}else{
	    for( i = 0; i < TEST_SIZE; ++i){
		register int j;
		for( j = 0; j < SIZE_MATERIAL; ++j){
		    if ( pointer->atributos[j].peso < 3 ) {
			switch( pointer->atributos[j].operador ) {
			    case IGUAL:
				valendo = ( pointer->atributos[j].valor == database_test[i][j] ) ? 1 : 0;
			    case DIFERENTE:
				valendo = ( pointer->atributos[j].valor != database_test[i][j] ) ? 1 : 0;
			    case MAIOR_IGUAL:
				valendo = ( pointer->atributos[j].valor >= database_test[i][j] ) ? 1 : 0;
			    case MENOR:
				valendo = ( pointer->atributos[j].valor < database_test[i][j] ) ? 1 : 0;
			}
		    }
		}
	    }
	}
	}*/
    pointer->evaluate = 0.0;
    pointer->true_positive = 0;
    pointer->true_negative = 0;
    pointer->false_positive = 0;
    pointer->false_negative = 0;
    calcula_indicadores(pointer);
    double specificity = ( pointer->true_positive + 1.0 ) / ( 1.0 * pointer->true_positive + 1.0 * pointer->false_negative + 1.0 ) ;
    double sensitivity = ( pointer->true_negative + 1.0 ) / ( 1.0 * pointer->true_negative + 1.0 * pointer->false_positive + 1.0 ) ;
    pointer->evaluate = specificity * sensitivity;
}

//FUNÇÕES JÁ DECLARADAS

INDI create_individuo_aleatorio(){
    INDI retorno;
    register int i = 0;
    for( ; i < SIZE_MATERIAL; ++i){
        //se for idade, sempre presente, se for historico familiar, presente ou nao, caso contrario pode ser 0, 1, 2 ou 3
        //retorno.atributos[i].presente = ( i == SIZE_MATERIAL - 1 ) ? 1 : ( i == 11 ) ? random() % 2 : random() % MAX_PRESENCA;
        //TODO: decidir, random() % 10 ou random () % 100; --> laura usou % 10
        retorno.atributos[i].peso = random() % 10;
        retorno.atributos[i].operador = random() % NUM_OPERADORES;
        retorno.atributos[i].valor = ( i == SIZE_MATERIAL - 1 ) ? random() % 80 : ( i == 11 ) ? random() % 2 : random() % NUM_OPERADORES;
    }
    retorno.false_positive = 0;
    retorno.false_negative = 0;
    retorno.true_positive = 0;
    retorno.true_negative = 0;
    retorno.evaluate = 0.0;
    return retorno;
}

//TODO: esse código precisa ser adaptado aos atributos
INDI create_individuo_com_material(ATRIB *atributos){
    INDI retorno;
    memcpy(retorno.atributos, atributos, SIZE_MATERIAL*sizeof(ATRIB));
    //avaliar_individuo(&retorno);
    return retorno;
}

/*INDI create_individuo_com_material(int *sendmory){
    INDI retorno;
    memcpy(retorno.sendmory, sendmory, SIZE_MATERIAL*sizeof(int));
    avaliar_individuo(&retorno);
    return retorno;
}*/

//TODO: consertar daqui pra baixo
//eu acho que esse código não precisa alterar
P_INDI copiar_individuo(P_INDI inicial){
    P_INDI retorno = calloc(1, sizeof(INDI));
    memcpy(retorno, inicial, sizeof(INDI));
    return retorno;
}

//eu acho que esse código não precisa alterar
P_INDI create_individuo_aleatorio_ponteiro(){
    P_INDI retorno = calloc(1, sizeof(INDI));
    *retorno = create_individuo_aleatorio();
    return retorno;
}

DP_INDI create_population(){
    DP_INDI pop = calloc(SIZE_POP, sizeof(P_INDI));
    register int i = 0;
    for ( ; i < SIZE_POP; ++i){
        pop[i] = create_individuo_aleatorio_ponteiro();
        while( populacao_contem_individuo(pop, pop[i], i) ){
            free(pop[i]);
            pop[i] = create_individuo_aleatorio_ponteiro();
        }
    }
    return pop;
}

DP_INDI copy_population(DP_INDI pop){
    DP_INDI retorno = calloc(SIZE_POP, sizeof(P_INDI));
    register int i = 0;
    for( ; i < SIZE_POP; ++i)
        retorno[i] = copiar_individuo(pop[i]);
    return retorno;
}

int compara_igualdade_individuos(P_INDI indi1, P_INDI indi2){
    return ( memcmp(indi1->atributos, indi2->atributos, SIZE_MATERIAL) == 0 );
}

int compara_avaliacao_individuos_const_void(const void *indi1, const void *indi2){
    P_INDI pi1 = *((DP_INDI) indi1);
    P_INDI pi2 = *((DP_INDI) indi2);
    return ( pi1->evaluate > pi2->evaluate ? 1 : ( pi1->evaluate == pi2->evaluate ? 0 : -1 ) );    
}

int compara_avaliacao_individuos_inverso_const_void(const void *indi1, const void *indi2){
    P_INDI pi1 = *((DP_INDI) indi1);
    P_INDI pi2 = *((DP_INDI) indi2);
    return ( pi1->evaluate < pi2->evaluate ? 1 : ( pi1->evaluate == pi2->evaluate ? 0 : -1 ) );    
}

void imprime_individuo(P_INDI pointer){
    register int i = 0, j = 0;
    printf("\tAtributos do individuo:\n\t");
    /*for ( ; i < SIZE_MATERIAL; ++i){
        //if ( pointer->atributos[i].peso < 3 )
       	//    printf("\t%3d\tPeso: %3.2f\t Operador: %s\t Valor: %3d\n",i, pointer->atributos[i].peso/10.0, ( pointer->atributos[i].operador == IGUAL ? "==" : pointer->atributos[i].operador == DIFERENTE ? "!=" : pointer->atributos[i].operador == MAIOR_IGUAL ? ">=" : "<"), pointer->atributos[i].valor);
        j += ( pointer->atributos[i].peso <= 3 ) ? 1 : 0;
    }
    printf("\t %d regras ativadas\tAvaliacao de %15.14f\n",j,pointer->evaluate);*/
    printf("\t\t%15.14f\n",pointer->evaluate);
}

void imprime_individuo_fast(P_INDI pointer){
    register int i = 0;
    printf("Atributos do individuo:\n");
    for ( ; i < SIZE_MATERIAL; ++i){
        //printf("%2d %s %2d\t",pointer->atributos[i].presente, ( pointer->atributos[i].operador == IGUAL ? "==" : pointer->atributos[i].operador == DIFERENTE ? "!=" : pointer->atributos[i].operador == MAIOR_IGUAL ? ">=" : "<"),pointer->atributos[i].valor);
        printf("%s %2d\t", ( pointer->atributos[i].operador == IGUAL ? "==" : pointer->atributos[i].operador == DIFERENTE ? "!=" : pointer->atributos[i].operador == MAIOR_IGUAL ? ">=" : "<"),pointer->atributos[i].valor);
    }
    printf("\n");
}

/*void imprime_individuo(P_INDI pointer){
    //register int i = 0;
    printf("S\tE\tN\tD\tM\tO\tR\tY\n\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\nAvaliação: %d\n",pointer->sendmory[0],pointer->sendmory[1],pointer->sendmory[2],pointer->sendmory[3],pointer->sendmory[4],pointer->sendmory[5],pointer->sendmory[6],pointer->sendmory[7], pointer->evaluate);
***    printf("Individuo:\t[\t");
    for ( ; i < SIZE_MATERIAL; ++i)
        printf("%3d\t",pointer->sendmory[i]);***
    printf("]\tAvaliacao: %d\n",pointer->evaluate);
}*/

void copy_population_to_other(DP_INDI p1, DP_INDI p2, int number){
    register int i = 0;
    for( ; i < number; ++i)
        p1[i] = copiar_individuo(p2[i]);
//    printf("Copiando %d individuos.\n",number);
}

